package com.hk.otasdk.fragments


import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.*
import android.widget.PopupWindow
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.hk.otasdk.MainApplication
import com.hk.otasdk.R
import com.hk.otasdk.activities.MainActivity
import com.hk.otasdk.dialog.DialogFileTransfer
import com.hk.otasdk.dialog.DialogFileTransferListener
import com.hk.otasdk.dialog.DialogOTA
import com.hk.otasdk.model.ota.OTAEnd
import com.hk.otasdk.model.ota.OTAState
import com.hk.otasdk.tool.bluetooth.BluetoothHelper
import com.hk.otasdk.tool.ota.ble.BleManager
import com.hk.otasdk.util.AppUtil
import com.hk.otasdk.util.FileTransferUtil
import com.hk.otasdk.util.OtaFileObserverHelper
import com.hk.otasdk.viewmodel.OTAViewModel
import com.jieli.component.utils.ToastUtil
import com.jieli.jlFileTransfer.Constants
import com.jieli.jlFileTransfer.FileUtils
import com.jieli.jlFileTransfer.WifiUtils
import com.jieli.jl_bt_ota.constant.StateCode
import com.jieli.jl_bt_ota.util.CHexConver
import com.jieli.jl_bt_ota.util.JL_Log
import kotlinx.android.synthetic.main.dialog_add_file_operation.view.*
import kotlinx.android.synthetic.main.dialog_file_operation.view.*
import kotlinx.android.synthetic.main.fragment_ota.*
import java.io.File
import java.io.IOException
import java.util.*
import kotlin.experimental.and


/**
 * 升级界面
 */
class OtaFragment : BaseFileFragment() {
    private lateinit var filePicker: ActivityResultLauncher<String>
    private lateinit var otaViewModel: OTAViewModel
    private lateinit var adapter: FileAdapter
    private var mIsFirstResume = true
    private var mDialogFileTransfer: DialogFileTransfer? = null;
    private var mIsHasStoragePermission = false
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mIsHasStoragePermission = AppUtil.isHasStoragePermission(context)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_ota, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        otaViewModel = ViewModelProvider(requireActivity()).get(OTAViewModel::class.java)
        initUI()
        observeCallback()
        filePicker = registerForActivityResult(ActivityResultContracts.GetContent()) {
            it?.let {
                try {
                    activity?.contentResolver?.let { contentResolver ->
                        val parentFilePath = MainApplication.getOTAFileDir()
                        var fileName = FileUtils.getFileName(context, it)
                        fileName =
                            FileTransferUtil.getNewUpgradeFileName(fileName, File(parentFilePath))
                        val saveFileDialog = DialogInputText().run {
                            this.title = this@OtaFragment.getString(R.string.save_file)
                            this.content = fileName
                            this.leftText = this@OtaFragment.getString(R.string.cancel)
                            this.rightText = this@OtaFragment.getString(R.string.save)
                            this.dialogClickListener = object : DialogClickListener {
                                override fun rightBtnClick(inputText: String?) {
                                    var inputFileNameStr: String = (inputText ?: "").trim()
                                    if (!inputFileNameStr.toUpperCase().endsWith(".UFW")) {
                                        ToastUtil.showToastShort(this@OtaFragment.getString(R.string.ufw_format_file_tips))
                                        return
                                    }
                                    val resultPath =
                                        parentFilePath + File.separator + inputFileNameStr
                                    if (File(resultPath).exists()) {
                                        ToastUtil.showToastShort(this@OtaFragment.getString(R.string.file_name_existed))
                                        return
                                    } else {
                                        FileUtils.copyFile(
                                            contentResolver.openInputStream(it),
                                            resultPath
                                        )
                                        Toast.makeText(
                                            context,
                                            R.string.please_refresh_web,
                                            Toast.LENGTH_LONG
                                        )
                                            .show()
                                    }
                                    this@run.dismiss()
                                }

                                override fun leftBtnClick(inputText: String?) {
                                    this@run.dismiss()
                                }
                            }
                            this
                        }
                        saveFileDialog.show(
                            this@OtaFragment.parentFragmentManager,
                            "scanFilterDialog"
                        )
                    }
//                            RxBus.get().post(Constants.RxBusEventType.LOAD_BOOK_LIST, 0)
                } catch (e: IOException) {
                    Toast.makeText(context, R.string.read_file_failed, Toast.LENGTH_SHORT)
                        .show()
                }
            }
        }
        updateOTAConnectionUI(otaViewModel.isConnected(), otaViewModel.getConnectedDevice())
        otaViewModel.readFileList()
    }

    override fun onResume() {
        super.onResume()
        if (mIsHasStoragePermission != AppUtil.isHasStoragePermission(context)) {//存储权限发生变化
            mIsHasStoragePermission = AppUtil.isHasStoragePermission(context)
            val tempPath = MainApplication.getOTAFileDir()
            OtaFileObserverHelper.getInstance().updateObserverPath(tempPath)
        }

        if (mIsFirstResume) {
            mIsFirstResume = false
            checkExternalStorage()
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        otaViewModel.destroy()
    }

    private fun initUI() {
        rv_file_list.layoutManager = LinearLayoutManager(requireContext())
        adapter = FileAdapter(false, mutableListOf())
        adapter.setOnItemClickListener { _, _, position ->
            adapter.setSelectedIndex(position)
        }
        adapter.setOnItemLongClickListener { adapter, view1, position ->
            val file: File = adapter.data.get(position) as File
            this.context?.run {
                val view = LayoutInflater.from(this).inflate(R.layout.dialog_file_operation, null)
                val popupWindow = PopupWindow(
                    view,
                    WindowManager.LayoutParams.WRAP_CONTENT,
                    WindowManager.LayoutParams.WRAP_CONTENT
                )
                popupWindow.isOutsideTouchable = true
                popupWindow.showAsDropDown(view1, 0, -100, Gravity.RIGHT or Gravity.BOTTOM)
                view.tv_delete_file.setOnClickListener {
                    if (file.exists()) {
                        file.delete()
                    }
                    popupWindow.dismiss()
                }
            }
            return@setOnItemLongClickListener false
        }
        val emptyView =
            LayoutInflater.from(requireContext()).inflate(R.layout.view_file_empty, null)
        adapter.setEmptyView(emptyView)
        rv_file_list.adapter = adapter

        ibtn_file_operation.setOnClickListener {
            checkExternalStorage(object : OnCheckExternalStorageEnvironmentCallback {
                override fun onSuccess() {
                    this@OtaFragment.context?.run {
                        val view =
                            LayoutInflater.from(this)
                                .inflate(R.layout.dialog_add_file_operation, null)
                        val popupWindow = PopupWindow(
                            view,
                            WindowManager.LayoutParams.WRAP_CONTENT,
                            WindowManager.LayoutParams.WRAP_CONTENT
                        )
                        popupWindow.isOutsideTouchable = true
                        popupWindow.showAsDropDown(it)
                        view.tv_upgrade_file_browse_local.setOnClickListener {
                            //  跳到本地文件浏览
                            filePicker.launch("application/octet-stream")
                            popupWindow.dismiss()
                        }
                        view.tv_upgrade_file_http_transfer.setOnClickListener {
                            //  传输升级文件 从电脑
                            popupWindow.dismiss()
                            mDialogFileTransfer = DialogFileTransfer()
                                .run {
                                val ipAddr: String = WifiUtils.getDeviceIpAddress()
                                val address = "http://$ipAddr:${Constants.HTTP_PORT}"
                                this.isCancelable = false
                                this.httpUrl = address
                                this.mListener = object :
                                    DialogFileTransferListener {
                                    override fun onLeftButtonClick() {
//                                WebService.stop(context)
                                        this@run.dismiss()
                                        mDialogFileTransfer = null
                                    }

                                    override fun onRightButtonClick() {
                                        val cm =
                                            context!!.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
                                        val mClipData = ClipData.newPlainText("Label", address)
                                        cm.setPrimaryClip(mClipData)
                                        Toast.makeText(
                                            context,
                                            context!!.getString(R.string.copy_toast),
                                            Toast.LENGTH_LONG
                                        ).show()
                                    }
                                }
                                this.show(this@OtaFragment.parentFragmentManager, "file_transfer")
                                this
                            }
                        }
                        view.tv_log_file_http_transfer.setOnClickListener {
                            // TODO: 传输Log文件到电脑
                            popupWindow.dismiss()
                        }
                    }

                }

                override fun onFailed() {
                }
            })
        }
        btn_upgrade.setOnClickListener {
            val pathList = adapter.getSelectedItems()
            JL_Log.w(TAG, "ota file size : ${pathList.size}")
            if (pathList.isEmpty()) {
                ToastUtil.showToastShort(getString(R.string.ota_please_chose_file))
                return@setOnClickListener
            }
            if (otaViewModel.getDeviceInfo() == null) {
                ToastUtil.showToastShort(getString(R.string.bt_not_connect_device))
                return@setOnClickListener
            }
            showOTADialog()
            otaViewModel.startOTA(pathList[0])
        }
    }

    private fun checkExternalStorage(callback: OnCheckExternalStorageEnvironmentCallback? = null): Unit {
        checkExternalStorageEnvironment(object : OnCheckExternalStorageEnvironmentCallback {
            override fun onSuccess() {
                unregisterOnCheckBluetoothEnvironmentCallback(this)
                otaViewModel.readFileList()
                callback?.onSuccess()
            }

            override fun onFailed() {
                unregisterOnCheckBluetoothEnvironmentCallback(this)
                callback?.onFailed()
            }
        })
    }

    private fun observeCallback() {
        otaViewModel.fileListMLD.observe(viewLifecycleOwner) { fileList ->
            JL_Log.i(TAG, "readFileList ---> ${fileList.size}")
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                fileList.sortWith { o1, o2 ->
                    if (o1.lastModified() > o2.lastModified()) {
                        -1
                    } else {
                        1
                    }
                }
            }
            adapter.onUpdateDataList(fileList)
            adapter.setNewInstance(fileList)
        }
        otaViewModel.otaConnectionMLD.observe(viewLifecycleOwner) { otaConnection ->
            requireActivity().run {
                JL_Log.d(TAG, "otaConnectionMLD : >>> $otaConnection")
                updateOTAConnectionUI(
                    otaConnection.state == StateCode.CONNECTION_OK,
                    otaConnection.device
                )
            }
        }
        otaViewModel.mandatoryUpgradeMLD.observe(viewLifecycleOwner) { device ->
            JL_Log.d(
                TAG, "mandatoryUpgradeMLD : >>> ${
                   AppUtil.printBtDeviceInfo(device)
                }"
            )
            (requireActivity() as MainActivity).switchSubFragment(1)
            ToastUtil.showToastShort(R.string.device_must_mandatory_upgrade)
        }
        otaViewModel.otaStateMLD.observe(viewLifecycleOwner) { otaState ->
            JL_Log.d(TAG, "otaStateMLD : >>> $otaState")
            if (otaState == null) return@observe
            requireActivity().run {
                when (otaState.state) {
                    OTAState.OTA_STATE_IDLE -> { //OTA结束
                        val otaEnd = otaState as OTAEnd
                        updateConnectedDeviceInfo(otaEnd.device)
                    }
                }
            }
        }
    }

    private fun updateOTABtn(isConnected: Boolean) {
        btn_upgrade?.let {
            it.isEnabled = isConnected
            it.visibility = if (otaViewModel.isOTA()) {
                View.INVISIBLE
            } else {
                View.VISIBLE
            }
            it.setBackgroundResource(
                if (isConnected) {
                    R.drawable.bg_btn_upgrade
                } else {
                    R.drawable.dbg_btn_unenable
                }
            )
        }
    }

    private fun updateOTAConnectionUI(isConnected: Boolean, device: BluetoothDevice?) {
        if (!isValidFragment()) return
        updateOTABtn(isConnected)
        updateConnectedDeviceInfo(device)
    }

    private fun updateConnectedDeviceInfo(device: BluetoothDevice?) {
        if (!isValidFragment()) return
        val isConnectedDevice = otaViewModel.isDeviceConnected(device)
        tv_connect_dev_name_vale?.let {
            it.text = if (isConnectedDevice) {
                AppUtil.getDeviceName(requireContext(), device)
            } else {
                ""
            }
        }
        tv_connect_dev_address_vale?.let {
            it.text = if (isConnectedDevice) {
                device!!.address
            } else {
                ""
            }
        }
        tv_connect_dev_type_vale?.let {
            it.text = if (isConnectedDevice) {
                getBtDeviceTypeString(AppUtil.getDeviceType(requireContext(), device))
            } else {
                ""
            }
        }
        tv_connect_status?.let {
            it.text = if (isConnectedDevice) {
                getString(R.string.device_status_connected)
              //  NpBleManager.getInstance().writeDataWithOutTips(currentTime())
            } else {
                getString(R.string.device_status_disconnected)
            }
           // val hexStr2Byte = hexStr2Byte("0165113A6B0000708002010101")
            if (isConnectedDevice) {
                writeDataToDeviceByBle(
                    BluetoothHelper.getInstance().getConnectedGatt(),
                    BleManager.BLE_UUID_SERVICE_NEW,
                    BleManager.BLE_UUID_WRITE_NEW,
                    currentTime()
                )
            }
        }
    }


    private fun writeDataToDeviceByBle(
        gatt: BluetoothGatt?,
        serviceUUID: UUID,
        characteristicUUID: UUID,
        data: ByteArray
    ): Boolean {
        if (gatt == null || data.size == 0 || !AppUtil.checkHasConnectPermission(
                this.context )
        ) {
            JL_Log.d(tag, "writeDataByBle : param is invalid.")
            return false
        }
        val gattService = gatt.getService(serviceUUID)
        if (null == gattService) {
            JL_Log.d(tag, "writeDataByBle : service is null.")
            return false
        }
        val gattCharacteristic = gattService.getCharacteristic(characteristicUUID)
        if (null == gattCharacteristic) {
            JL_Log.d(tag, "writeDataByBle : characteristic is null")
            return false
        }
        var ret = false
        try {
            gattCharacteristic.value = data
            ret = gatt.writeCharacteristic(gattCharacteristic)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        JL_Log.d(
            tag,
            "新蕊新蕊 writeDataByBle : send ret : " + ret + ", data = " + CHexConver.byte2HexStr(gattCharacteristic.value)
        )
        Log.e(TAG, "Timer2 == writeDataByBle =>" + byte2HexStr(gattCharacteristic.value))
        return ret
    }
  private  fun byte2HexStr(data: ByteArray?): String {
        if (data == null || data.size < 1) return " "
        val sb = StringBuilder()
        for (b in data) {
            sb.append(String.format("%02X", (b and 0xff.toByte())))
        }
        return sb.toString()
    }

    private  fun hexStr2Byte(hexString: String): ByteArray {
        val len = hexString.length
        val result = ByteArray(len / 2)
        var i = 0
        while (i < len) {
            result[i / 2] = (hexString.substring(i, i + 2).toInt(16) + 0).toByte()
            i += 2
        }
        return result
    }


    /**
     * 获取当前时间
     *
     * @return
     */
    private fun currentTime(): ByteArray {
        val data = ByteArray(13)
        val t = (System.currentTimeMillis() / 1000).toInt()
        var index = 0
        data[index++] = 0x01
        data[index++] = (t shr 24 and 0xff).toByte()
        data[index++] = (t shr 16 and 0xff).toByte()
        data[index++] = (t shr 8 and 0xff).toByte()
        data[index++] = (t and 0xff).toByte()
        val dateTime = Calendar.getInstance().timeZone.getOffset(Date().time).toLong()
        data[index++] = (dateTime / 1000 shr 24 and 0xff).toByte()
        data[index++] = (dateTime / 1000 shr 16 and 0xff).toByte()
        data[index++] = (dateTime / 1000 shr 8 and 0xff).toByte()
        data[index++] = (dateTime / 1000 and 0xff).toByte()

        data[index++] = (1 and 0xff).toByte()
        data[index++] = (1 and 0xff).toByte()
        data[index++] = (1 and 0xff).toByte()
        data[index++] = (1 and 0xff).toByte() // android 1; ios 2
        return data
    }

    private fun getBtDeviceTypeString(type: Int?): String {
        if (type == null) return ""
        var typeName = ""
        when (type) {
            BluetoothDevice.DEVICE_TYPE_CLASSIC -> typeName =
                getString(R.string.device_type_classic)
            BluetoothDevice.DEVICE_TYPE_LE -> typeName = getString(R.string.device_type_ble)
            BluetoothDevice.DEVICE_TYPE_DUAL -> typeName = getString(R.string.device_type_dual_mode)
            BluetoothDevice.DEVICE_TYPE_UNKNOWN -> typeName =
                getString(R.string.device_type_unknown)
        }
        return typeName
    }

    private fun showOTADialog(): Unit {
        DialogOTA().run {
            this.isCancelable = false
            this.show(this@OtaFragment.parentFragmentManager, "file_transfer")
            this
        }
    }
}